package core;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map.Entry;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import tournamentData.GeneralTournamentInformation;
import tournamentData.GlobalScore;
import tournamentData.Target;
import tournamentData.TournamentData;

import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

import dataBase.DbHolder;

public class XMLWriter {

	public XMLWriter() {

	}

	public static String getFileName(String tournamentName, Date startDate) {
		StringBuilder output = new StringBuilder();
		output.append(tournamentName.replace(" ", "_"));
		output.append("_");
		output.append(startDate.toString());
		output.append(".xml");

		return output.toString();
	}

	public static void writeToFile(File file) throws SAXException, IOException {

		FileOutputStream outputStream = new FileOutputStream(file);
		// if (lastRead == null) {
		// outputStream = new
		// FileOutputStream(XMLWriter.getFileName(TournamentData.getInstance().getGeneralTournamentInformation().getTournamentName(),
		// TournamentData.getInstance().getGeneralTournamentInformation().getStartDate()).toString());
		// } else {
		// outputStream =
		// }

		OutputFormat outputFormat = new OutputFormat("XML", "UTF-8", true);
		outputFormat.setIndent(1);
		outputFormat.setIndenting(true);
		// outputFormat.setDoctype(null, "users.dtd");
		XMLSerializer serializer = new XMLSerializer(outputStream, outputFormat);

		// SAX2.0 ContentHandler.
		ContentHandler contentHandler = serializer.asContentHandler();
		contentHandler.startDocument();

		AttributesImpl attributes = new AttributesImpl();
		attributes.clear();

		/** general wrapper */
		contentHandler.startElement("", "", XMLConstants.TOURNAMENT, attributes);

		/** tournament type */
		contentHandler.startElement("", "", XMLConstants.TOURNAMENT_TYPE, attributes);
		contentHandler.characters(TournamentData.getInstance().getGeneralTournamentInformation().getTournamentType().toCharArray(), 0, TournamentData.getInstance().getGeneralTournamentInformation()
				.getTournamentType().length());
		contentHandler.endElement("", "", XMLConstants.TOURNAMENT_TYPE);

		/** tournament name */
		contentHandler.startElement("", "", XMLConstants.TOURNAMENT_NAME, attributes);
		contentHandler.characters(TournamentData.getInstance().getGeneralTournamentInformation().getTournamentName().toCharArray(), 0, TournamentData.getInstance().getGeneralTournamentInformation()
				.getTournamentName().length());
		contentHandler.endElement("", "", XMLConstants.TOURNAMENT_NAME);

		/** start date */
		contentHandler.startElement("", "", XMLConstants.START_DATE, attributes);
		contentHandler.characters(TournamentData.getInstance().getGeneralTournamentInformation().getStartDate().toString().toCharArray(), 0, TournamentData.getInstance()
				.getGeneralTournamentInformation().getStartDate().toString().length());
		contentHandler.endElement("", "", XMLConstants.START_DATE);

		/** end date */
		contentHandler.startElement("", "", XMLConstants.END_DATE, attributes);
		contentHandler.characters(TournamentData.getInstance().getGeneralTournamentInformation().getEndDate().toString().toCharArray(), 0, TournamentData.getInstance()
				.getGeneralTournamentInformation().getEndDate().toString().length());
		contentHandler.endElement("", "", XMLConstants.END_DATE);

		/** operator */
		contentHandler.startElement("", "", XMLConstants.OPERATOR, attributes);
		contentHandler.characters(TournamentData.getInstance().getGeneralTournamentInformation().getOrganizer().toCharArray(), 0, TournamentData.getInstance().getGeneralTournamentInformation()
				.getOrganizer().length());
		contentHandler.endElement("", "", XMLConstants.OPERATOR);

		/** tournament lead */
		contentHandler.startElement("", "", XMLConstants.LEADER, attributes);
		contentHandler.characters(TournamentData.getInstance().getGeneralTournamentInformation().getTournamentLead().toCharArray(), 0, TournamentData.getInstance().getGeneralTournamentInformation()
				.getTournamentLead().length());
		contentHandler.endElement("", "", XMLConstants.LEADER);

		/** referee */
		contentHandler.startElement("", "", XMLConstants.REFEREE, attributes);
		contentHandler.characters(TournamentData.getInstance().getGeneralTournamentInformation().getReferee().toCharArray(), 0, TournamentData.getInstance().getGeneralTournamentInformation()
				.getReferee().length());
		contentHandler.endElement("", "", XMLConstants.REFEREE);

		/** participants */
		contentHandler.startElement("", "", XMLConstants.PARTICIPANTS, attributes);
		for (int i = 0; i < TournamentData.getInstance().getAllParticipants().length; i++) {
			contentHandler.startElement("", "", XMLConstants.PARTICIPANT, attributes);
			contentHandler.startElement("", "", XMLConstants.ID, attributes);
			String id = new Integer(TournamentData.getInstance().getAllParticipants() [i]).toString();
			contentHandler.characters(id.toCharArray(), 0, id.length());
			contentHandler.endElement("", "", XMLConstants.ID);
			contentHandler.startElement("", "", XMLConstants.NAME, attributes);
			Person person = DbHolder.getInstance().getArcherData().getById(TournamentData.getInstance().getAllParticipants() [i]);
			String name = person.getName() + " " + person.getSurName();
			contentHandler.characters(name.toCharArray(), 0, name.length());
			contentHandler.endElement("", "", XMLConstants.NAME);
			contentHandler.endElement("", "", XMLConstants.PARTICIPANT);
		}
		contentHandler.endElement("", "", XMLConstants.PARTICIPANTS);

		/** number of targets */
		contentHandler.startElement("", "", XMLConstants.NUMBEROFTARGETS, attributes);
		String numberOfTargets = new Integer(TournamentData.getInstance().getNumberOfTargets()).toString();
		contentHandler.characters(numberOfTargets.toCharArray(), 0, numberOfTargets.length());
		contentHandler.endElement("", "", XMLConstants.NUMBEROFTARGETS);

		/** players per targets */
		contentHandler.startElement("", "", XMLConstants.PLAYERSPERTARGET, attributes);
		String playersPerTarget = new Integer(TournamentData.getInstance().getPlayersPerTarget()).toString();
		contentHandler.characters(playersPerTarget.toCharArray(), 0, playersPerTarget.length());
		contentHandler.endElement("", "", XMLConstants.PLAYERSPERTARGET);

		/** target assignment */
		contentHandler.startElement("", "", XMLConstants.TARGETASSIGNMENTS, attributes);
		Set < Entry < Integer, Target >> entrySet = TournamentData.getInstance().getTargetAssignment().getAllTargetAssignments();

		Iterator < Entry < Integer, Target >> iterator = entrySet.iterator();
		while (iterator.hasNext()) {
			Entry < Integer, Target > entry = iterator.next();
			String id = new Integer(entry.getKey()).toString();
			String targetNumber = new Integer(entry.getValue().getNumber()).toString();
			String targetPosition = new Character(entry.getValue().getCharacter()).toString();
			contentHandler.startElement("", "", XMLConstants.TARGETASSIGNMENT, attributes);
			contentHandler.startElement("", "", XMLConstants.ID, attributes);
			contentHandler.characters(id.toCharArray(), 0, id.length());
			contentHandler.endElement("", "", XMLConstants.ID);
			contentHandler.startElement("", "", XMLConstants.TARGETNUMBER, attributes);
			contentHandler.characters(targetNumber.toCharArray(), 0, targetNumber.length());
			contentHandler.endElement("", "", XMLConstants.TARGETNUMBER);
			contentHandler.startElement("", "", XMLConstants.TARGETPOSITION, attributes);
			contentHandler.characters(targetPosition.toCharArray(), 0, targetPosition.length());
			contentHandler.endElement("", "", XMLConstants.TARGETPOSITION);
			contentHandler.endElement("", "", XMLConstants.TARGETASSIGNMENT);
		}

		contentHandler.endElement("", "", XMLConstants.TARGETASSIGNMENTS);

		/** Scores */
		contentHandler.startElement("", "", XMLConstants.SCORES, attributes);
		for (int i = 0; i < TournamentData.getInstance().getAllParticipants().length; i++) {
			contentHandler.startElement("", "", XMLConstants.SCORE, attributes);
			contentHandler.startElement("", "", XMLConstants.ID, attributes);
			String id = new Integer(TournamentData.getInstance().getAllParticipants() [i]).toString();
			contentHandler.characters(id.toCharArray(), 0, id.length());
			contentHandler.endElement("", "", XMLConstants.ID);

			GlobalScore toSave = TournamentData.getInstance().getScore(TournamentData.getInstance().getAllParticipants() [i]);
			LinkedList < Integer > savedRounds = toSave.getSavedRounds();
			contentHandler.startElement("", "", XMLConstants.ROUNDS, attributes);
			for (int j = 0; j < savedRounds.size(); j++) {
				contentHandler.startElement("", "", XMLConstants.RESULT, attributes);

				contentHandler.startElement("", "", XMLConstants.ROUND, attributes);
				String round = new Integer(savedRounds.get(j)).toString();
				contentHandler.characters(round.toCharArray(), 0, round.length());
				contentHandler.endElement("", "", XMLConstants.ROUND);

				contentHandler.startElement("", "", XMLConstants.NINES, attributes);
				String nines = new Integer(TournamentData.getInstance().getScore(TournamentData.getInstance().getAllParticipants() [i]).getNines(savedRounds.get(j))).toString();
				contentHandler.characters(nines.toCharArray(), 0, nines.length());
				contentHandler.endElement("", "", XMLConstants.NINES);

				contentHandler.startElement("", "", XMLConstants.TENS, attributes);
				String tens = new Integer(TournamentData.getInstance().getScore(TournamentData.getInstance().getAllParticipants() [i]).getTens(savedRounds.get(j))).toString();
				contentHandler.characters(tens.toCharArray(), 0, tens.length());
				contentHandler.endElement("", "", XMLConstants.TENS);

				contentHandler.startElement("", "", XMLConstants.HITS, attributes);
				String hits = new Integer(TournamentData.getInstance().getScore(TournamentData.getInstance().getAllParticipants() [i]).getXs(savedRounds.get(j))).toString();
				contentHandler.characters(hits.toCharArray(), 0, hits.length());
				contentHandler.endElement("", "", XMLConstants.HITS);

				contentHandler.startElement("", "", XMLConstants.POINTS, attributes);
				String points = new Integer(TournamentData.getInstance().getScore(TournamentData.getInstance().getAllParticipants() [i]).getPoints(savedRounds.get(j))).toString();
				contentHandler.characters(points.toCharArray(), 0, points.length());
				contentHandler.endElement("", "", XMLConstants.POINTS);

				contentHandler.endElement("", "", XMLConstants.RESULT);
			}
			contentHandler.endElement("", "", XMLConstants.ROUND);
			contentHandler.endElement("", "", XMLConstants.SCORE);
		}

		contentHandler.endElement("", "", XMLConstants.SCORES);

		contentHandler.endElement("", "", XMLConstants.TOURNAMENT.toString());

		contentHandler.endDocument();
		outputStream.close();
	}

	private static void insertGeneralTournamentInformation(Document doc) {

		String tournamentType = doc.getElementsByTagName(XMLConstants.TOURNAMENT_TYPE).item(0).getTextContent();
		String tournamentName = doc.getElementsByTagName(XMLConstants.TOURNAMENT_NAME).item(0).getTextContent();
		Date startDate = new Date(doc.getElementsByTagName(XMLConstants.START_DATE).item(0).getTextContent());
		Date endDate = new Date(doc.getElementsByTagName(XMLConstants.END_DATE).item(0).getTextContent());
		String organizer = doc.getElementsByTagName(XMLConstants.OPERATOR).item(0).getTextContent();
		String tournamentLead = doc.getElementsByTagName(XMLConstants.LEADER).item(0).getTextContent();
		String referee = doc.getElementsByTagName(XMLConstants.REFEREE).item(0).getTextContent();

		GeneralTournamentInformation generalInformation = new GeneralTournamentInformation(tournamentType, tournamentName, startDate, endDate, organizer, tournamentLead, referee);
		TournamentData.getInstance().setGeneralTournamentInformation(generalInformation);
	}

	private static void insertParticipants(Document doc) {
		NodeList participants = doc.getElementsByTagName(XMLConstants.PARTICIPANTS);
		for (int i = 0; i < participants.item(0).getChildNodes().getLength(); i++) {
			if (participants.item(0).getChildNodes().item(i).getNodeName().equals(XMLConstants.PARTICIPANT)) {
				Node participant = participants.item(0).getChildNodes().item(i);

				for (int j = 0; j < participant.getChildNodes().getLength(); j++) {
					if (participant.getChildNodes().item(j).getNodeName().equals(XMLConstants.ID)) {
						String id = participant.getChildNodes().item(j).getTextContent();
						TournamentData.getInstance().addRemovePlayer(Integer.parseInt(id));
					}
				}
			}
		}
	}

	private static void insertTargetInformation(Document doc) {
		String numberOfTargets = doc.getElementsByTagName(XMLConstants.NUMBEROFTARGETS).item(0).getTextContent();
		TournamentData.getInstance().setNumberOfTargets(Integer.parseInt(numberOfTargets));

		String playersPerTarget = doc.getElementsByTagName(XMLConstants.PLAYERSPERTARGET).item(0).getTextContent();
		TournamentData.getInstance().setPlayersPerTarget(Integer.parseInt(playersPerTarget));
	}

	private static void insertTargetAssignments(Document doc) {
		NodeList targetAssignments = doc.getElementsByTagName(XMLConstants.TARGETASSIGNMENTS);
		for (int i = 0; i < targetAssignments.item(0).getChildNodes().getLength(); i++) {
			if (targetAssignments.item(0).getChildNodes().item(i).getNodeName().equals(XMLConstants.TARGETASSIGNMENT)) {
				Node participant = targetAssignments.item(0).getChildNodes().item(i);

				String id = participant.getChildNodes().item(1).getTextContent();
				String targetNumber = participant.getChildNodes().item(3).getTextContent();
				String position = participant.getChildNodes().item(5).getTextContent();
				TournamentData.getInstance().getTargetAssignment().assignTarget(Integer.parseInt(id), new Target(Integer.parseInt(targetNumber), position.charAt(0)));
			}
		}
	}

	private static void insertScores(Document doc) {
		Node scores = doc.getElementsByTagName(XMLConstants.SCORES).item(0);
		for (int i = 0; i < scores.getChildNodes().getLength(); i++) {
			if (scores.getChildNodes().item(i).getNodeName().equals(XMLConstants.SCORE)) {
				Node score = scores.getChildNodes().item(i);

				for (int j = 0; j < score.getChildNodes().getLength(); j++) {
					int playerID = Integer.parseInt(score.getChildNodes().item(1).getTextContent());

					if (score.getChildNodes().item(j).getNodeName().equals(XMLConstants.ROUNDS)) {
						Node rounds = score.getChildNodes().item(j);
						for (int k = 0; k < rounds.getChildNodes().getLength(); k++) {

							if (rounds.getChildNodes().item(k).getNodeName().equals(XMLConstants.RESULT)) {
								Node result = rounds.getChildNodes().item(k);

								int round = Integer.parseInt(result.getChildNodes().item(1).getTextContent());
								int nines = Integer.parseInt(result.getChildNodes().item(3).getTextContent());
								int tens = Integer.parseInt(result.getChildNodes().item(5).getTextContent());
								int hits = Integer.parseInt(result.getChildNodes().item(7).getTextContent());
								int points = Integer.parseInt(result.getChildNodes().item(9).getTextContent());

								TournamentData.getInstance().getScore(playerID).setNines(round, nines);
								TournamentData.getInstance().getScore(playerID).setTens(round, tens);
								TournamentData.getInstance().getScore(playerID).setHits(round, hits);
								TournamentData.getInstance().getScore(playerID).setPoints(round, points);
							}
						}
					}
				}
			}
		}
	}

	public static void readFromFile(File file) throws ParserConfigurationException, SAXException, IOException {

		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document doc = db.parse(file);
		doc.getDocumentElement().normalize();

		insertGeneralTournamentInformation(doc);
		insertParticipants(doc);
		insertTargetInformation(doc);
		insertTargetAssignments(doc);
		insertScores(doc);

	}
}
